// 后续考虑分立协议部分独立出来

#include "tss_writer.h"
#include "link.h"
// #include "components\trng\trng.h"
#include "polsys.h"

typedef enum _cmd32
{
    CMD32_GET_BASIC = 0X21,
    CMD32_GET_PARAM = 0X22,
    CMD32_SET_PARAM = 0X23
}cmd32_t;




static void (*mCb)(tw_ev_t e) = NULL;
static polsys_task_t mTWTimer;
static tw_status_t Tw_mStatus = TW_ST_NO_CON;
static u8 mWaitCommTime = 0;
static u8 mSendBuff[32];
static u16 * mParam;
static u8 mTimeOut = 0;
static u8 RyTryTimes = 0;
static tw_dev_info_t mDeviceInfo;
static volatile bool mMeasuring = false;    // 标记正在测量

static void Encrypt(const u8 *scr, u8 * dst);
static bool IsSame(const u8 *dat1, const u8 *dat2, int len);
static void TssWriterTimer();
static void SendIdentify();
static void SendCmdNP(u8 cmd);
static void SendSetParam(const u16 * param);
static void LoadParam(output_mode_t mode, const u8 * orig);
static u8 VoltageToLevel(u16 Voltage);
static void GetMoreDevInfo(u8 * idf_pkg);




void _Link_Event(link_ev_t e)
{
    u8 * ret = Link_GetPkg();
    bool base_dev = false;
    switch(e)
    {
    case LINK_EV_RECEIVED_PKG_TO:   // 仅防伪，没有后续数据的最基础量产机
        base_dev = true;
    case LINK_EV_RECEIVED_PKG:
        mWaitCommTime = 0;
        switch(Tw_mStatus)
        {
        case TW_ST_IDFING:
            Encrypt(mSendBuff + 3, mSendBuff + 8);  // 利用 mSendBuff 未使用空间存放算出的密码
            if(IsSame(ret, mSendBuff + 8, 8))   // 正品验证通过
            {
                if(base_dev)mDeviceInfo.Type = DEV_TYPE_NM_BASE;
                else mDeviceInfo.Type = (dev_type_t)ret[9];

                if(Link_GetLastRecPort() == LINK_PORT_DP)
                {
                    mDeviceInfo.CommPort = COMM_PORT_DP;
                }
                else mDeviceInfo.CommPort = COMM_PORT_VBUS;

                Tw_mStatus = TW_ST_GENUINE;
                if(mCb)mCb(TW_EV_IDENTIFIED);       // 此处执行时间过长会导致GetBase命令发送太迟而失败

                GetMoreDevInfo(ret);    // 获取更多产品信息
            }
            else
            {
                Tw_mStatus = TW_ST_FAKE;
                _TW_StartPlugCheck();  // 开始接入/拔出检测
                if(mCb)mCb(TW_EV_UNKNOW_DEV);
            }
            break;
        case TW_ST_RE_IDFING_WR:
            Tw_mStatus = TW_ST_WRITE_WAIT;
            // _TW_SetVBusLow();  // 让VBUS输出低电平
            break;
        case TW_ST_RE_IDFING_RD:
            Tw_mStatus = TW_ST_READ_WAIT;
            // _TW_SetVBusLow();  // 让VBUS输出低电平
            break;
        case TW_ST_GETTING_BASIC:
            mDeviceInfo.ReportBattVoltage = MAKE_U16(ret[7],ret[8]);// mV
            mDeviceInfo.ReportRes = MAKE_U16(ret[9],ret[10]);
            mDeviceInfo.BattLoadVoltage = MAKE_U16(ret[11],ret[12]);// mV
            mDeviceInfo.BattLevel = VoltageToLevel(mDeviceInfo.ReportBattVoltage);
            // 开始测量获取信息
            Tw_mStatus = TW_ST_MEASURING;
            mMeasuring = true;
            _TW_StartMeasuring();   // 开始测量，之后 TW_EvMeasDone 会被调用。
            break;
        case TW_ST_WRITING:
            Tw_mStatus = TW_ST_GENUINE;
            
            if(ret[3] == 0) // 写入成功
            {
                if(mCb)mCb(TW_EV_WR_OK);
            }
            else        // 写入失败
            {
                if(mCb)mCb(TW_EV_WR_FAIL);
            }
            break;
        case TW_ST_READING:
            Tw_mStatus = TW_ST_GENUINE;
            
            if(ret[3] == 0) // 读取成功
            {
                output_mode_t mode = (output_mode_t)ret[4];
                if(mode == OUTPUT_MODE_VRMS || mode == OUTPUT_MODE_POWER)   // 目前仅支持有效电压和功率
                {
                    LoadParam(mode, ret + 5); // 加载参数到传入的缓存
                    if(mCb)mCb(TW_EV_RD_OK);
                }
                else    // 不支持的输出模式
                {
                    if(mCb)mCb(TW_EV_RD_NONE);
                }
            }
            else        // 读取失败
            {
                if(mCb)mCb(TW_EV_RD_FAIL);
            }
            break;
        case TW_ST_GENUINE:
            if(ret[3] == 0x03)  // 从机退出通信模式
            {
                Tw_mStatus = TW_ST_GENUINE_TO;
                _TW_StartPlugCheck();  // 开始接入/拔出检测
            }
            break;
        default : break;
        }
        break;
    case LINK_EV_SLOW_PORT:
        mTimeOut = 255;
        if(mCb)mCb(TW_EV_SLOW_PORT);
        break;
    default: break;
    }
}

// 间隔10ms执行一次
static void TssWriterTimer()
{
    switch(Tw_mStatus)
    {
    case TW_ST_NO_CON: // 未连接，正在检测接入
        if(_TW_IsPlugIn()) // 有设备接入
        {
            memset(&mDeviceInfo, 0xff, sizeof(mDeviceInfo));
            Tw_mStatus = TW_ST_CON;
            Link_SetPlugStatus(true);
            if(mCb)mCb(TW_EV_PLUG_IN);
            mWaitCommTime = 0;
            RyTryTimes = 0;
        }
        break;
    case TW_ST_CON:    // 有设备接入，等待尝试通信
        if(!_TW_IsPlugIn()) // 设备移除
        {
            Tw_mStatus = TW_ST_NO_CON;
            Link_SetPlugStatus(false);
            if(mCb)mCb(TW_EV_PLUG_OUT);
        }
        else
        {
            mWaitCommTime++;
            if(mWaitCommTime == 50)
            {
                SendIdentify();
                Tw_mStatus = TW_ST_IDFING;
                mWaitCommTime = 0;
            }
        }
        break;
    case TW_ST_IDFING:        // 尝试通信识别，正在等待回应
        mWaitCommTime++;
        if(mWaitCommTime == mTimeOut)   // 等待回应超时了
        {
            if(RyTryTimes == 3)
            {
                if(mCb)mCb(TW_EV_UNKNOW_DEV);
            }
            // Tw_mStatus = TW_ST_FAKE;
            Tw_mStatus = TW_ST_CON; // 始终重试识别是否正品
            _TW_StartPlugCheck();  // 开始接入/拔出检测
            mWaitCommTime = 0;
            if(RyTryTimes < 255)RyTryTimes++;
        }
        break;
    case TW_ST_RE_IDFING_WR:   // 尝试重新通信识别（重新同步建立连接），正在等待回应
        mWaitCommTime++;
        if(mWaitCommTime == mTimeOut)   // 等待回应超时了
        {
            Tw_mStatus = TW_ST_GENUINE_TO;
            _TW_StartPlugCheck();  // 开始接入/拔出检测
            if(mCb)mCb(TW_EV_WR_FAIL);
            mWaitCommTime = 0;
        }
        break;
    case TW_ST_RE_IDFING_RD:   // 尝试重新通信识别（重新同步建立连接），正在等待回应
        mWaitCommTime++;
        if(mWaitCommTime == mTimeOut)   // 等待回应超时了
        {
            Tw_mStatus = TW_ST_GENUINE_TO;
            _TW_StartPlugCheck();  // 开始接入/拔出检测
            if(mCb)mCb(TW_EV_RD_FAIL);
            mWaitCommTime = 0;
        }
        break;
    case TW_ST_GENUINE:    // 接口上连接的是正品
        mWaitCommTime++;
        if(mWaitCommTime == mTimeOut)   // 等待退出通信包超时
        {
            Tw_mStatus = TW_ST_GENUINE_TO;
            _TW_StartPlugCheck();  // 开始接入/拔出检测
            mWaitCommTime = 0;
        }
        break;
    case TW_ST_FAKE:       // 接口上连接的是仿品
    case TW_ST_GENUINE_TO:  // 正品，但是长时间未通信已经断开通信连接
        if(!_TW_IsPlugIn()) // 设备移除
        {
            Tw_mStatus = TW_ST_NO_CON;
            Link_SetPlugStatus(false);
            if(mCb)mCb(TW_EV_PLUG_OUT);
        }
        break;
    case TW_ST_WRITE_WAIT:
        mWaitCommTime++;
        if(mWaitCommTime == 3)
        {
            Tw_mStatus = TW_ST_WRITING;
            SendSetParam(mParam);
            mWaitCommTime = 0;
        }
        break;
    case TW_ST_READ_WAIT:
        mWaitCommTime++;
        if(mWaitCommTime == 3)
        {
            Tw_mStatus = TW_ST_READING;
            SendCmdNP(CMD32_GET_PARAM);
            mWaitCommTime = 0;
        }
        break;
    case TW_ST_GET_BASIC_WAIT:
        mWaitCommTime++;
        if(mWaitCommTime == 3)
        {
            Tw_mStatus = TW_ST_GETTING_BASIC;
            SendCmdNP(CMD32_GET_BASIC);
            mWaitCommTime = 0;
        }
        break;
    case TW_ST_WRITING:    // 正在写入数据，成功或者失败后有回应包
        mWaitCommTime++;
        if(mWaitCommTime == mTimeOut)   // 等待回应超时
        {
            Tw_mStatus = TW_ST_GENUINE;
            if(mCb)mCb(TW_EV_WR_FAIL);
            mWaitCommTime = 0;
        }
        break;
    case TW_ST_MEASURING:
        if(mMeasuring == false) // 测量已经结束
        {
            Tw_mStatus = TW_ST_GENUINE;     // 只有正品刚连接时才会触发测量，所以测量结束后状态切换回正品已连接
            if(mCb)mCb(TW_EV_INFO_UPDATE);  // 通知产品信息已经更新
        }
        break;
    default: break;
    }
}




void TW_EvMeasDone(u16 load_res, u16 batt_voltage)
{
    mDeviceInfo.MeasuRes = load_res;
    if(mDeviceInfo.CommPort == LINK_PORT_DP)
    {
        mDeviceInfo.MeasuBattVoltage = batt_voltage;
    }
    if(mDeviceInfo.Type == DEV_TYPE_NM_BASE)
    {
        mDeviceInfo.BattLevel = VoltageToLevel(mDeviceInfo.MeasuBattVoltage);
    }
    mMeasuring = false;
}

void TW_Init()
{
    Link_Init();
    _TW_StartPlugCheck();
    Polsys_TaskInit(&mTWTimer, TssWriterTimer, 10 / POLSYS_TICK_CYCLE);
}

IN_LINE void TW_SetCb(void (*new_cb)(tw_ev_t e))
{
    mCb = new_cb;
}

IN_LINE tw_status_t TW_GetStatus()
{
    return Tw_mStatus;
}

IN_LINE tw_dev_info_t * TW_GetDevInfo()
{
    return &mDeviceInfo;
}

bool TW_StartWrite(u16 * param)
{
    mWaitCommTime = 0;
    mParam = param;

    if(mDeviceInfo.Type < DEV_TYPE_DEFAULT ) return false;  // 量产机

    if(Tw_mStatus == TW_ST_GENUINE)
    {
        SendSetParam(param);
        Tw_mStatus = TW_ST_WRITING;
        return true;
    }
    else if(Tw_mStatus == TW_ST_GENUINE_TO)
    {
        SendIdentify();
        Tw_mStatus = TW_ST_RE_IDFING_WR;
        return true;
    }
    return false;
}

bool TW_StartRead(u16 * param)
{
    mWaitCommTime = 0;
    mParam = param;

    if(mDeviceInfo.Type < DEV_TYPE_DEFAULT ) return false;  // 量产机

    if(Tw_mStatus == TW_ST_GENUINE)
    {
        SendCmdNP(CMD32_GET_PARAM);
        Tw_mStatus = TW_ST_READING;
        return true;
    }
    else if(Tw_mStatus == TW_ST_GENUINE_TO)
    {
        SendIdentify();
        Tw_mStatus = TW_ST_RE_IDFING_RD;
        return true;
    }
    return false;
}


// 以下内部函数


static void SendIdentify()
{
    u32 rand = Board_GetRand();
    mSendBuff[0] = 0X00;
    mSendBuff[1] = 0X55;
    mSendBuff[2] = 0XAA;
    mSendBuff[3] = *((u8 *)&rand);
    mSendBuff[4] = *((u8 *)&rand + 1);
    mSendBuff[5] = *((u8 *)&rand + 2);
    mSendBuff[6] = *((u8 *)&rand + 3);
    Link_SendIdentify(mSendBuff,7);
    mTimeOut = 50;      // 默认只等待500ms，若收到慢速接口事件，改为等待2550ms
}

// 发送无参数的命令
static void SendCmdNP(u8 cmd)
{
    mSendBuff[0] = 0X00;
    mSendBuff[1] = 0X55;
    mSendBuff[2] = 0XAA;
    mSendBuff[3] = 0X00;
    mSendBuff[4] = 1;
    mSendBuff[5] = cmd;    
    Link_Send(mSendBuff, 6);
    mTimeOut = 50;  // 默认只等待500ms，若收到慢速接口事件，改为等待2000ms
}

// 发送SetParam(0X23)命令。
// param - 参数（mW）,在这里依据模式转换参数单位
static void SendSetParam(const u16 * param)
{
    u8 i,index;

    mSendBuff[0] = 0x00;
    mSendBuff[1] = 0x55;
    mSendBuff[2] = 0xAA;
    mSendBuff[3] = 0X00;
    mSendBuff[4] = 22;
    mSendBuff[5] = CMD32_SET_PARAM;    // V3.2协议写入参数命令
    mSendBuff[6] = mDeviceInfo.SugOutputMode;   // 输出模式
    index = 7;
    if(mDeviceInfo.SugOutputMode == OUTPUT_MODE_POWER)
    {
        if(mDeviceInfo.ComParam_f.ByteFm)
        {
            for(i=0;i<10;i++)
            {
                mSendBuff[index++] = param[i]>>8;
                mSendBuff[index++] = param[i];
            }
        }
        else
        {
            // memcpy(mSendBuff + index, (u8 *)param, 20);
            // index += 20;
            for(i=0;i<10;i++)
            {
                mSendBuff[index++] = param[i];
                mSendBuff[index++] = param[i]>>8;
            }
        }
    }
    else //if(mDeviceInfo.SugOutputMode == OUTPUT_MODE_VRMS)
    {
        u16 vrms;
        if(mDeviceInfo.ComParam_f.ByteFm)
        {
            for(i=0;i<10;i++)
            {
                vrms = IntSqrt(param[i] * mDeviceInfo.DesignRes); // mV
                mSendBuff[index++] = vrms >> 8;
                mSendBuff[index++] = vrms;
            }
        }
        else
        {
            for(i=0;i<10;i++)
            {
                vrms = IntSqrt(param[i] * mDeviceInfo.DesignRes); // mV
                mSendBuff[index++] = vrms;
                mSendBuff[index++] = vrms >> 8;
            }
        }
    }

    Link_Send(mSendBuff, index);
    mTimeOut = 50;  // 默认只等待500ms，若收到慢速接口事件，改为等待2000ms
}

// 加载读取到的输出参数到传入的缓存mParam
static void LoadParam(output_mode_t mode, const u8 * orig)
{
    u8 i;
    u8 index = 0;

    if(mode == OUTPUT_MODE_POWER)
    {
        if(mDeviceInfo.ComParam_f.ByteFm)
        {
            for(i=0;i<10;i++)
            {
                u8 h = orig[index++];
                u8 l = orig[index++];
                mParam[i] = MAKE_U16(h, l);
            }
        }
        else
        {
            for(i=0;i<10;i++)
            {
                u8 l = orig[index++];
                u8 h = orig[index++];
                mParam[i] = MAKE_U16(h, l);
            }
            // memcpy(mParam, orig, 20);
        }
    }
    else //if(mode == OUTPUT_MODE_VRMS)
    {
        u16 vrms;
        if(mDeviceInfo.ComParam_f.ByteFm)
        {
            for(i=0;i<10;i++)
            {
                u8 h = orig[index++];
                u8 l = orig[index++];
                vrms = MAKE_U16(h, l) + 1;  // mV +1避免定点计算出现14999这种数据
                mParam[i] = (u32)vrms * vrms / mDeviceInfo.DesignRes; // mW
            }
        }
        else
        {
            for(i=0;i<10;i++)
            {
                u8 l = orig[index++];
                u8 h = orig[index++];
                vrms = MAKE_U16(h, l) + 1;  // mV +1避免定点计算出现14999这种数据
                // vrms = (u16 *(orig))[i] + 1;
                mParam[i] = (u32)vrms * vrms / mDeviceInfo.DesignRes; // mW
            }
        }
    }
}


static bool IsSame(const u8 *dat1, const u8 *dat2, int len)
{
    u8 i;

    for(i=0;i<len;i++)
    {
        if(dat1[i] != dat2[i])return false;
    }
    return true;
}



// 把4字节数据通过一系列运算得出8字节数据
// scr: 4字节输入数据
// dst: 8字节输出数据
static void Encrypt(const u8 *scr, u8 * dst)
{
    u8 i;
    for(i = 0; i < 4; i++)
    {
        dst[i] = scr[i];
    }
    dst[4] = dst[1];
    dst[5] = dst[2];
    dst[6] = dst[3];
    dst[7] = dst[0];
}

#define _TW_MAX_BV  4200
static const u16 mVolToDlTable[] = {3350, 3450, 3550, 3600, 3650, 3675, 3700, 3850, _TW_MAX_BV - 300, _TW_MAX_BV - 200, _TW_MAX_BV - 100, _TW_MAX_BV - 50};
									//   0     5     10    20    30    40    50    60    70    				80    			90    			100
//单纯通过电压（mV）粗略计算电量
static u8 VoltageToLevel(u16 Voltage)
{
	u8 temp, i;
	const u16 *table;

    if(Voltage >= _TW_MAX_BV + 500) return 255;

	table = mVolToDlTable;

	for (i = 0; i < 12; i++)
	{
		if (Voltage < table[i])
			break;
	}
	i--;
	if (i == 255) //电压小于3V
	{
		temp = 0;
	}
	else if (i >= 2)
	{
		temp = (i - 1) * 10;
		temp += (Voltage - table[i]) * 10 / (table[i + 1] - table[i]);
	}
	else //0,1
	{
		temp = i * 5;
		temp += (Voltage - table[i]) * 5 / (table[i + 1] - table[i]);
	}
	return temp;
}

// 获取更多产品信息
// idf_pkg： 识别包数据
static void GetMoreDevInfo(u8 * idf_pkg)
{
    if(mDeviceInfo.Type == DEV_TYPE_NM_BASE)
    {
        Tw_mStatus = TW_ST_MEASURING;
        mMeasuring = true;
        _TW_StartMeasuring();   // 开始测量，之后 TW_EvMeasDone 会被调用。
    }
    else if(mDeviceInfo.Type == DEV_TYPE_IM_BASE)
    {
        // 先从识别包中获取信息
        u8 adc_bits = idf_pkg[10];
        u16 adv_full = IntPow(2, adc_bits) - 1;
        u8 mos_res = idf_pkg[11];   // mΩ
        u16 vref = MAKE_U16(idf_pkg[12], idf_pkg[13]);  // mV
        u16 adv_vref;
        u16 adv_vref_load;
        u16 adv_out;
        if(adc_bits<=8)
        {
            adv_vref = idf_pkg[14];
            adv_vref_load = idf_pkg[15];
            adv_out = idf_pkg[16];
        }
        else
        {
            adv_vref = MAKE_U16(idf_pkg[14], idf_pkg[15]);
            adv_vref_load = MAKE_U16(idf_pkg[16], idf_pkg[17]);
            adv_out = MAKE_U16(idf_pkg[18], idf_pkg[19]);
        }
        mDeviceInfo.ReportBattVoltage = (u32)vref * adv_full / adv_vref;   // mV
        mDeviceInfo.BattLoadVoltage = (u32)vref * adv_full / adv_vref_load;  // mV
        mDeviceInfo.ReportRes = (u32)mos_res * adv_out / (adv_full - adv_out);  //mΩ
        mDeviceInfo.BattLevel = VoltageToLevel(mDeviceInfo.ReportBattVoltage);
        // 开始测量获取信息
        Tw_mStatus = TW_ST_MEASURING;
        mMeasuring = true;
        _TW_StartMeasuring();   // 开始测量，之后 TW_EvMeasDone 会被调用。
    }
    else if(mDeviceInfo.Type == DEV_TYPE_FM_BASE)
    {
        // 先从识别包中获取信息
        u8 adc_bits = idf_pkg[10];
        u16 adv_full = IntPow(2, adc_bits) - 1;
        u8 mos_res = idf_pkg[11];   // mΩ
        u16 vref = MAKE_U16(idf_pkg[12], idf_pkg[13]);  // mV
        u8 res_h = idf_pkg[14];     // VDD通道的分压电阻比例
        u8 res_l = idf_pkg[15];
        u16 adv_vdd;
        u16 adv_vdd_load;
        u16 adv_out;
        if(adc_bits<=8)
        {
            adv_vdd = idf_pkg[16];
            adv_vdd_load = idf_pkg[17];
            adv_out = idf_pkg[18];
        }
        else
        {
            adv_vdd = MAKE_U16(idf_pkg[16], idf_pkg[17]);
            adv_vdd_load = MAKE_U16(idf_pkg[18], idf_pkg[19]);
            adv_out = MAKE_U16(idf_pkg[20], idf_pkg[21]);
        }
        mDeviceInfo.ReportBattVoltage = (u32)adv_vdd * vref * (res_l + res_h) / res_l / adv_full;// mV
        mDeviceInfo.BattLoadVoltage = (u32)adv_vdd_load * vref * (res_l + res_h) / res_l / adv_full;// mV
        mDeviceInfo.ReportRes = mos_res * adv_out / (adv_full - adv_out);//mΩ
        mDeviceInfo.BattLevel = VoltageToLevel(mDeviceInfo.ReportBattVoltage);
        // 开始测量获取信息
        Tw_mStatus = TW_ST_MEASURING;
        mMeasuring = true;
        _TW_StartMeasuring();   // 开始测量，之后 TW_EvMeasDone 会被调用。
    }
    else if (mDeviceInfo.Type == DEV_TYPE_DEFAULT)
    {
        // 先从识别包中获取信息
        mDeviceInfo.Timeout = MAKE_U16(idf_pkg[10],idf_pkg[11]);
        mDeviceInfo.MaxRxLen = MAKE_U16(idf_pkg[12],idf_pkg[13]);
        mDeviceInfo.ComParam = 0X01;
        mDeviceInfo.Model = TW_MODEL_UNKNOW;
        mDeviceInfo.SugOutputMode = OUTPUT_MODE_VRMS; // 恒压有效值
        mDeviceInfo.DesignRes = 800; // 0.8Ω
        // 开始发送 GetBasic 命令获取更多信息
        Tw_mStatus = TW_ST_GET_BASIC_WAIT;
        // _TW_SetVBusLow();  // 让VBUS输出低电平，确保下一次发送数据成功，！！！！！！！放这里不合适
    }
    else if(mDeviceInfo.Type == DEV_TYPE_NORMAL)
    {
        // 先从识别包中获取信息
        mDeviceInfo.Timeout = MAKE_U16(idf_pkg[10],idf_pkg[11]);
        mDeviceInfo.MaxRxLen = MAKE_U16(idf_pkg[12],idf_pkg[13]);
        mDeviceInfo.ComParam = idf_pkg[14];
        mDeviceInfo.Model = (tw_model_t)idf_pkg[15];
        mDeviceInfo.SugOutputMode = (output_mode_t)idf_pkg[16];
        mDeviceInfo.DesignRes = (u16)idf_pkg[17] * 10;
        // 开始发送 GetBasic 命令获取更多信息
        Tw_mStatus = TW_ST_GET_BASIC_WAIT;
        // _TW_SetVBusLow();  // 让VBUS输出低电平
    }
}

